#include "LineReplicationGraphObject.h"

#include "LineReplicationGraphSetting.h"
#include "Component/LineReplicationGraphComponent.h"
#include "GameFramework/GameModeBase.h"
#include "Node/LRG_Node_AlwaysRelevant_ForConnection.h"
#include "Node/LRG_Node_GridSpatial2D.h"


ULineReplicationGraph* ULineReplicationGraph::Get(const UObject* WorldContext)
{
	//if(ULRG_GameModeComponent* Component = ULRG_GameModeComponent::Get(WorldContext))
	//{
	//	return Cast<ULineReplicationGraph>( Component->GetReplicationDriver() );
	//}
	return nullptr;
}

ULineReplicationGraph::ULineReplicationGraph()
{

}

void ULineReplicationGraph::ResetGameWorldState()
{
	Super::ResetGameWorldState();

	AlwaysRelevantStreamingLevelActors.Empty();

	for (UNetReplicationGraphConnection* ConnManager : Connections)
	{
		for (UReplicationGraphNode* ConnectionNode : ConnManager->GetConnectionGraphNodes())
		{
			if (ULRG_Node_AlwaysRelevant_ForConnection* AlwaysRelevantConnectionNode = Cast<ULRG_Node_AlwaysRelevant_ForConnection>(ConnectionNode))
			{
				AlwaysRelevantConnectionNode->ResetGameWorldState();
			}
		}
	}

	for (UNetReplicationGraphConnection* ConnManager : PendingConnections)
	{
		for (UReplicationGraphNode* ConnectionNode : ConnManager->GetConnectionGraphNodes())
		{
			if (ULRG_Node_AlwaysRelevant_ForConnection* AlwaysRelevantConnectionNode = Cast<ULRG_Node_AlwaysRelevant_ForConnection>(ConnectionNode))
			{
				AlwaysRelevantConnectionNode->ResetGameWorldState();
			}
		}
	}
}

void ULineReplicationGraph::InitGlobalActorClassSettings()
{
	Super::InitGlobalActorClassSettings();

	UEnum* Enum = StaticEnum<ELineReplicationType>();
	UE_LOG(LogReplicationGraph, Log, TEXT("========================================InitGlobalActorClassSettings========================================"));

	TMap<ELineReplicationType, TArray<UClass*> > ClassRepNodePoliciesRecorder;//纪录,用于日志输出
	auto SetClassReqNode = [&]( UClass* Class, ELineReplicationType Mapping)
	{
		if(ClassRepNodePolicies.Contains(Class, false)){
			UE_LOG(LogReplicationGraph, Error, TEXT("SetClassReqNode :%s Setted"), *Class->GetName() );
		}
		ClassRepNodePolicies.Set(Class, Mapping);
		
		auto* Array = ClassRepNodePoliciesRecorder.Find(Mapping);
		if(!Array)
		{
			Array = &ClassRepNodePoliciesRecorder.Add(Mapping);
		}
		Array->Add(Class);
	};
	auto SetReplicationType = [&](ELineReplicationType ReplicationType, const TSet<UClass*>& ClassArray)
	{
		for(UClass* Class : ClassArray) {
			SetClassReqNode(Class, ReplicationType);
		}
	};
	SetReplicationType(ELineReplicationType::NotRouted, NotRoutedClasses);
	SetReplicationType(ELineReplicationType::RelevantAllConnections, RelevantAllServerClasses);
	SetReplicationType(ELineReplicationType::RelevantAllConnectionsInLine, RelevantAllInLineClasses);
	
	SetReplicationType( ELineReplicationType::RelevantAllConnections,
		{
			AGameModeBase::StaticClass()
	});
	
	TArray<UClass*> AllReplicatedClasses;
	for (TObjectIterator<UClass> It; It; ++It)
	{
		UClass* Class = *It;
		AActor* ActorCDO = Cast<AActor>(Class->GetDefaultObject());
		if (!ActorCDO || !ActorCDO->GetIsReplicated())
		{
			continue;
		}
		AllReplicatedClasses.Add(Class);
		if (ClassRepNodePolicies.Contains(Class, false))
		{
			continue;
		}
		if(ELineReplicationType* TypePtr = ClassRepNodePolicies.Get(Class))
		{
			SetClassReqNode(Class, *TypePtr);
			continue;
		}
		if (!(ActorCDO->bAlwaysRelevant || ActorCDO->bOnlyRelevantToOwner || ActorCDO->bNetUseOwnerRelevancy))
		{
			SetClassReqNode(Class, ELineReplicationType::Spatialize_Dynamic);
		}
		else if (ActorCDO->bAlwaysRelevant && !ActorCDO->bOnlyRelevantToOwner)
		{
			SetClassReqNode(Class, ELineReplicationType::RelevantAllConnectionsInLine);
		}
	}
	for (auto KeyValue : ClassRepNodePoliciesRecorder)
	{
		UE_LOG(LogReplicationGraph, Log, TEXT("ReplicationType::%s"), *Enum->GetNameStringByValue(static_cast<uint32>(KeyValue.Key)) );
		if(KeyValue.Value.IsEmpty())
		{
			continue;
		}
		for(UClass* Class : KeyValue.Value)
		{
			UE_LOG(LogReplicationGraph, Log, TEXT("\t\t%s"), *Class->GetName() );
		}
	}
	TArray<UClass*> ExplicitlySetClasses;	//记录被特殊设置的Class
	auto SetClassInfo = [&](UClass* Class, const FClassReplicationInfo& Info) { GlobalActorReplicationInfoMap.SetClassInfo(Class, Info); ExplicitlySetClasses.Add(Class); };
	
	auto InitClassReplicationInfo = [](FClassReplicationInfo& Info, UClass* Class, bool bSpatialize, float ServerMaxTickRate)
	{
		UE_LOG(LogReplicationGraph, Log, TEXT("%s"), *Class->GetName());
		AActor* CDO = Class->GetDefaultObject<AActor>();
		if (bSpatialize)
		{
			Info.SetCullDistanceSquared(CDO->NetCullDistanceSquared);
			UE_LOG(LogReplicationGraph, Log, TEXT("\tCullDistance=%f"), Info.GetCullDistance());
		}
		Info.ReplicationPeriodFrame = FMath::Max<uint32>( (uint32)FMath::RoundToFloat(ServerMaxTickRate / CDO->NetUpdateFrequency), 1);
		UE_LOG(LogReplicationGraph, Log, TEXT("\tReplicationPeriodFrame=%d,NetUpdateFrequency=%f"), Info.ReplicationPeriodFrame, CDO->NetUpdateFrequency);
	};

	FClassReplicationInfo PawnClassRepInfo;
	PawnClassRepInfo.SetCullDistanceSquared(PawnCullDistance * PawnCullDistance);
	SetClassInfo( APawn::StaticClass(), PawnClassRepInfo );

	UReplicationGraphNode_ActorListFrequencyBuckets::DefaultSettings.ListSize = 20;
	
	UE_LOG(LogReplicationGraph, Log, TEXT("==========InitClassReplicationInfo=========="));
	for (UClass* ReplicatedClass : AllReplicatedClasses)
	{
		if (ExplicitlySetClasses.FindByPredicate([&](const UClass* SetClass) { return ReplicatedClass->IsChildOf(SetClass); }) != nullptr)
		{
			continue;
		}
		const ELineReplicationType* Mapping = ClassRepNodePolicies.Get(ReplicatedClass);
		bool bClassIsSpatialize = false;
		if(Mapping)
		{
			bClassIsSpatialize = (*Mapping >= ELineReplicationType::Spatialize_Static);
		}
		FClassReplicationInfo ClassInfo;
		InitClassReplicationInfo(ClassInfo, ReplicatedClass, bClassIsSpatialize, NetDriver->NetServerMaxTickRate);
		GlobalActorReplicationInfoMap.SetClassInfo( ReplicatedClass, ClassInfo );
	}
	DestructInfoMaxDistanceSquared = DestructionInfoMaxDist * DestructionInfoMaxDist;
	UE_LOG(LogReplicationGraph, Log, TEXT("==========InitGlobalActorClassSettings End=========="));
}

void ULineReplicationGraph::InitGlobalGraphNodes()
{
	const ULineReplicationGraphSetting* Setting = ULineReplicationGraphSetting::Get();
	GridSpatialNode = CreateNewNode<ULRG_Node_GridSpatial>();	//初始化 GridSpatialization2D
	GridSpatialNode->CellSize = Setting->GridCellSize;
	AddGlobalGraphNode(GridSpatialNode);

	AlwaysRelevantNode = CreateNewNode<UReplicationGraphNode_ActorList>();
	AddGlobalGraphNode(AlwaysRelevantNode);

	AlwaysRelevantToLineNode = CreateNewNode<UReplicationGraphNode_ActorList>();
	AddGlobalGraphNode(AlwaysRelevantToLineNode);
}

void ULineReplicationGraph::InitConnectionGraphNodes(UNetReplicationGraphConnection* RepGraphConnection)
{
	Super::InitConnectionGraphNodes(RepGraphConnection);

	ULRG_Node_AlwaysRelevant_ForConnection* AlwaysRelevantNodeForConnection = CreateNewNode<ULRG_Node_AlwaysRelevant_ForConnection>();

	RepGraphConnection->OnClientVisibleLevelNameAdd.AddUObject(AlwaysRelevantNodeForConnection, &ULRG_Node_AlwaysRelevant_ForConnection::OnClientLevelVisibilityAdd);
   	RepGraphConnection->OnClientVisibleLevelNameRemove.AddUObject(AlwaysRelevantNodeForConnection, &ULRG_Node_AlwaysRelevant_ForConnection::OnClientLevelVisibilityRemove);
	
	AddConnectionGraphNode(AlwaysRelevantNodeForConnection, RepGraphConnection);
}

void ULineReplicationGraph::AddNetworkActor(AActor* Actor)
{
	LLM_SCOPE_BYTAG(NetRepGraph);
	QUICK_SCOPE_CYCLE_COUNTER(UReplicationGraph_AddNetworkActor);
	
	if (IsActorValidForReplicationGather(Actor) == false)
	{
		return;
	}
	if (NetDriver && !NetDriver->ShouldReplicateActor(Actor))
	{
		return;
	}
	bool bWasAlreadyThere = false;
	ActiveNetworkActors.Add(Actor, &bWasAlreadyThere);
	if (bWasAlreadyThere)
	{
		// Guarding against double adds
		return;
	}

	// Create global rep info	
	FGlobalActorReplicationInfo& GlobalInfo = GlobalActorReplicationInfoMap.Get(Actor);
	GlobalInfo.bWantsToBeDormant = Actor->NetDormancy > DORM_Awake;

	RouteAddNetworkActorToNodes(FNewReplicatedActorInfo(Actor), GlobalInfo);
}

void ULineReplicationGraph::RouteAddNetworkActorToNodes(const FNewReplicatedActorInfo& ActorInfo, FGlobalActorReplicationInfo& GlobalInfo)
{
	ULineReplicationGraphComponent* LineComponent = ULineReplicationGraphComponent::Get(ActorInfo.GetActor());
	ELineReplicationType ReplicationType;
	if(!LineComponent)
	{
		ReplicationType = GetClassReplicationType(ActorInfo.Class);
		if(ReplicationType == ELineReplicationType::NotRouted)
		{
			return;
		}
		LineComponent = ULineReplicationGraphComponent::Create(ActorInfo.GetActor());
		LineComponent->SetReplicationType( ReplicationType );
	}
	else
	{
		ReplicationType = LineComponent->GetReplicationType();
	}
	LineComponent->SetReplicationInfo( GlobalInfo );
	LineComponent->SetNewReplicatedActorInfo(ActorInfo);
	
	switch(ReplicationType)
	{
		case ELineReplicationType::NotRouted:
			break;
		case ELineReplicationType::RelevantAllConnections:
			if (ActorInfo.StreamingLevelName == NAME_None)
			{
				AlwaysRelevantNode->NotifyAddNetworkActor(ActorInfo);
			}
			else
			{
				FActorRepListRefView& RepList = AlwaysRelevantStreamingLevelActors.FindOrAdd(ActorInfo.StreamingLevelName);
				RepList.ConditionalAdd(ActorInfo.Actor);
			}
		break;
			break;
		case ELineReplicationType::RelevantAllConnectionsInLine:
			AlwaysRelevantToLineNode->NotifyAddNetworkActor(ActorInfo);
			break;
		case ELineReplicationType::Spatialize_Static:
			GridSpatialNode->AddActor_Static(ActorInfo, GlobalInfo);
			break;
		case ELineReplicationType::Spatialize_Dynamic:
			GridSpatialNode->AddActor_Dynamic(LineComponent);
			break;
		case ELineReplicationType::Spatialize_Dormancy:
			GridSpatialNode->AddActor_Dormancy(ActorInfo, GlobalInfo);
			break;
		default: ;
	}
}

void ULineReplicationGraph::RouteRemoveNetworkActorToNodes(const FNewReplicatedActorInfo& ActorInfo)
{
	ULineReplicationGraphComponent* LineComponent = ULineReplicationGraphComponent::Get(ActorInfo.GetActor());
	if(!LineComponent)
	{
		return;
	}
	LineComponent->OnRemoveFromNetwork();
	switch(const ELineReplicationType ReplicationType = LineComponent->GetReplicationType())
	{
	case ELineReplicationType::NotRouted:
		break;
	case ELineReplicationType::RelevantAllConnections:
		if (ActorInfo.StreamingLevelName == NAME_None)
		{
			AlwaysRelevantNode->NotifyRemoveNetworkActor(ActorInfo);
		}
		else
		{
			FActorRepListRefView& RepList = AlwaysRelevantStreamingLevelActors.FindOrAdd(ActorInfo.StreamingLevelName);
			RepList.RemoveFast(ActorInfo.Actor);
		}
		break;
		break;
	case ELineReplicationType::RelevantAllConnectionsInLine:
		break;
	case ELineReplicationType::Spatialize_Static:
		GridSpatialNode->RemoveActor_Static(ActorInfo);
		break;
	case ELineReplicationType::Spatialize_Dynamic:
		GridSpatialNode->RemoveActor_Dynamic(LineComponent);
		break;
	case ELineReplicationType::Spatialize_Dormancy:
		GridSpatialNode->RemoveActor_Dormancy(ActorInfo);
		break;
	default: ;
	}
}

int32 ULineReplicationGraph::ServerReplicateActors(float DeltaSeconds)
{
	return Super::ServerReplicateActors(DeltaSeconds);
}

ELineReplicationType ULineReplicationGraph::GetClassReplicationType(UClass* Class)
{
	if(const ELineReplicationType* ReplicationType = ClassRepNodePolicies.Get(Class))
	{
		return *ReplicationType;
	}
	return ELineReplicationType::NotRouted;
}

void ULineReplicationGraph::AddDebugComponent(ULRG_DebugComponent* Component)
{
	DebugComponents.Add(Component);
}

void ULineReplicationGraph::RemoveDebugComponent(ULRG_DebugComponent* Component)
{
	DebugComponents.Remove(Component);
}